#include "pso.h"
#include <limits.h>
//TEST
   #include <cstdlib> 
   #include <ctime> 
//ENDTEST

      /**
       * Constructor por defecto
       * */
       PSO::PSO(){}

   
      /** Constructor
       * @param listado de los rectángulos a empaquetar
       * */
       PSO::PSO(list<Rectangulo> lista, Contenedor & C_) : Heuristica (lista) {
          //cout << "PSO::Constructor(listado) con lista de tamaño= " << lista.size() << endl;
          S= maxS;
          C = C_;
       }

       

      /** Generar solución inicial 
      * Hay que generar S soluciones iniciales y asignarle
      * a cada partícula sus datos necesarios
      * */
      list<Rectangulo>  PSO::generaInicial() {
         //cout << "PSO::generaInicial()" << endl;
         //srand((unsigned)time(0));
         //srand(12345);
         particulas = generaConjuntoSoluciones(S);
			for (int i = 0; i < S; i++)
				b.push_back(particulas[i]);
         mejor = particulas[0].getLista();
         mejorSol = INT_MAX;
         return b[0].getLista();
      }


      /**
      * Mejorar solución
      *     Actualiza los valores de cada partícula
      * */
      list<Rectangulo> PSO::mejoraSolucion() {
         list <Rectangulo> temp = mejor;
         //cout << endl << "PSO::mejoraSolucion(). Iteracion: " << iteracion << 
                           //"  Iteración sin mejora: " << iteracionSinCambio << endl;
         //Fase de Evaluación
         for (int i = 0; i < S; i++) {
            //PASOS:
             //Calcular el ¿valor? de cada solucion
             int valor = C.calcularAltura(particulas[i].getLista());
             particulas[i].setValor(valor);
              //Si el valor es mejor que su mejor anterior lo actualizamos
             if (valor < b[i].getValor())
               b[i] = particulas[i];
             
             //Si el valos es mejor que el mejor de todos (g = mejorSol) actualizamos mejorSol y mejor
				if (valor < mejorSol) {					
               mejorSol = valor;
               mejor = particulas[i].copyLista();
               temp = particulas[i].copyLista();
               iteracionSinCambio = 0;
					//if (iteracion != 1)
						//cout << "He encontrado una mejor en la iteracion: " << iteracion << 
									//", particula(" << i << 
									//"), valor: " << valor <<
                           //", mejorSol: " << mejorSol <<
									//endl;               
            }
         }
         
         //Fase de Actualización
         srand((unsigned)time(0));
         vector<Solucion> g = vector<Solucion>();
         for (int i = 0; i < S; i++) {            
            //Buscar el mínimo de su entorno y ese es g[i] 
            g.push_back(minimoDelEntorno(i));

            //Actualizar x[i] utilizando sugerencia de atractores
            //    50% al mejor del entorno
            //    30% al mejor anterior
            //    5% sin cambio
            //    15% al mejor absoluto
            int aleatorio = rand() % 100;
            Solucion seguida = Solucion(mejor,mejorSol);
            if (aleatorio < 30) { //Nos movemos a la mejor anterior
               seguida = b[i];
            } else if ((aleatorio >= 30) && (aleatorio < 80)) { //Nos movemos al mejor del entorno
               seguida = g[i];
            } else if ((aleatorio >= 80) && (aleatorio < 85)) { //No nos movemos
               seguida = particulas[i];
            } //else { va al mejor absoluto

            combinaSoluciones(seguida,particulas[i],seguida.getLista().size()/2,-1);

         } 
         //cout << "mejorSol = " << mejorSol << endl;
         return temp;
      }
      
      
       bool PSO::parada(int valor) {
          iteracion++;
          //cout << "ITERACION: " << iteracion << " con valor: " << valor << endl << endl << endl;
          iteracionSinCambio++;

          if (iteracion == MAX_ITERACIONES) {
            cout << "Alcanzado máximo de iteraciones: " << MAX_ITERACIONES  << endl;
            return true;
          }
         if (iteracionSinCambio == MAX_ITERACIONES_SIN_CAMBIO) {
            cout << "Alcanzado máximo de iteraciones sin mejora: " << MAX_ITERACIONES_SIN_CAMBIO << endl;
            return true;
         }
         return false;
      }
      
      
      /**
       * Devuelve la mejor partícula del entorno:
       * Entorno puede ser:
       *    Aleatorio.
       *    Anillo
       *    Estrella
       * Usaremos de momento el aleatorio 
       * */    
      Solucion PSO::minimoDelEntorno(int k) {
         Solucion minimo = particulas[k];
         for (int i = 0; i < TAM_ENTORNO; i++) {
            //int aleatorio = rand() % particulas.size();
            //Solucion sAleatoria = particulas[aleatorio];
            //if (sAleatoria.getValor() < minimo.getValor())
               //minimo = sAleatoria;     
            int indice = ((k - (i-(TAM_ENTORNO)/2)) + S) % S;
               //cout << "k= " << k << "   Accedo a la particula " << indice << " de " << S << endl;
            if (particulas[indice].getValor() < minimo.getValor()) 
               minimo = particulas[indice];
         }
         return minimo;
      }

      
      PSO::~PSO() { }
